package agenda.impl;

import lib.api.ConjuntoStringTDA;
import lib.api.DiccionarioMultipleABBTDA;
import lib.impl.ConjuntoStringImpl;

public class DiccionarioMultipleABBImpl implements DiccionarioMultipleABBTDA {

	// -------------- Comienzo de Representacion Interna (no modificar)
	// --------------
	private class NodoABB {
		int clave;
		ConjuntoStringTDA valores;
		DiccionarioMultipleABBTDA hijoIzq;
		DiccionarioMultipleABBTDA hijoDer;
	}

	private NodoABB raiz;

	// -------------- fin de representacion interna
	// ---------------------------------

	public void InicializarDiccionario() {
		raiz = null;
	}

	public boolean DiccionarioVacio() {
		return raiz == null;
	}

	public int RaizClave() {
		return raiz.clave;
	}

	/**
	 * Creo dos conjuntos 
	 * - uno para no perder el conjunto
	 * - y el otro para el resultado
	 * - vacio los valores de la raiz uno por uno y los voy llenando en ambos conjuntos
	 * - una vez que no quedan mas valores, a raiz.valores le asigno el objeto aux. y devuelvo el objeto que es en definitiva una copia del conjunto.
	 */
	public ConjuntoStringTDA RaizValores() {
		ConjuntoStringTDA resultado = new ConjuntoStringImpl();
		ConjuntoStringTDA aux = new ConjuntoStringImpl();
		resultado.InicializarConjunto();
		aux.InicializarConjunto();
		while(!raiz.valores.ConjuntoVacio()){
			String s = raiz.valores.Elegir();
			resultado.Agregar(s);
			aux.Agregar(s);
			raiz.valores.Sacar(s);
		}
		raiz.valores = aux;
		return resultado;
	}

	public DiccionarioMultipleABBTDA HijoDer() {
		return raiz.hijoDer;
	}

	public DiccionarioMultipleABBTDA HijoIzq() {
		return raiz.hijoIzq;
	}

	public void Agregar(int clave, String valor) {
		if (raiz == null || raiz.clave == clave) {
			// Nodo vacio
			if (raiz == null) {
				raiz = new NodoABB();

				ConjuntoStringTDA valores = new ConjuntoStringImpl();
				valores.InicializarConjunto();
				valores.Agregar(valor);
				raiz.clave = clave;
				raiz.valores = valores;
				raiz.hijoDer = new DiccionarioMultipleABBImpl();
				raiz.hijoDer.InicializarDiccionario();
				raiz.hijoIzq = new DiccionarioMultipleABBImpl();
				raiz.hijoIzq.InicializarDiccionario();
			} else {
				// existe clave
				// CORRECION PUNTO 5, no hace falta verificar si existe o no, si existe el agregar no lo agrega y listo :D
				//if (!raiz.valores.Pertenece(valor)) {
					raiz.valores.Agregar(valor);
				//}
			}
		} else if (raiz.clave > clave) {
			raiz.hijoIzq.Agregar(clave, valor);
		} else if (raiz.clave < clave) {
			raiz.hijoDer.Agregar(clave, valor);
		}
	}

	public void Eliminar(int clave) {
		NodoABB nodoaux = new NodoABB();
		DiccionarioMultipleABBTDA aux;
		if (raiz != null) {
			if (raiz.clave == clave && raiz.hijoIzq.DiccionarioVacio()
					&& raiz.hijoDer.DiccionarioVacio()) {
				raiz = null;
			} else if (raiz.clave == clave && !raiz.hijoIzq.DiccionarioVacio()) {
				aux = this.mayor(raiz.hijoIzq);
				nodoaux.clave = aux.RaizClave();
				nodoaux.valores = aux.RaizValores();
				nodoaux.hijoDer = raiz.hijoDer;
				nodoaux.hijoIzq = raiz.hijoIzq;
				raiz.hijoIzq.Eliminar(nodoaux.clave);
				raiz = nodoaux;
			} else if (raiz.clave == clave && raiz.hijoIzq.DiccionarioVacio()) {
				aux = this.menor(raiz.hijoDer);
				nodoaux.clave = aux.RaizClave();
				nodoaux.valores = aux.RaizValores();
				nodoaux.hijoDer = raiz.hijoDer;
				nodoaux.hijoIzq = raiz.hijoIzq;
				raiz.hijoDer.Eliminar(nodoaux.clave);
				raiz = nodoaux;
			} else if (raiz.clave < clave) {
				raiz.hijoDer.Eliminar(clave);
			} else {
				raiz.hijoIzq.Eliminar(clave);
			}

		}
	}

	/**
	 * @param a
	 * @return ConjuntoStringTDA Metodo que devuelve los valores del hijo mayor
	 *         de un nodo.
	 */
	/*
	 * private ConjuntoStringTDA mayor(DiccionarioMultipleABBTDA a){
	 */
	private DiccionarioMultipleABBTDA mayor(DiccionarioMultipleABBTDA a) {
		if (a.HijoDer().DiccionarioVacio()) {
			return a;
		} else {
			return mayor(a.HijoDer());
		}
	}

	/**
	 * @param a
	 * @return ConjuntoStringTDA Metodo que devuelve los valores del hijo menor
	 *         de un nodo.
	 */
	/*
	 * private ConjuntoStringTDA menor(DiccionarioMultipleABBTDA a){
	 */
	private DiccionarioMultipleABBTDA menor(DiccionarioMultipleABBTDA a) {
		if (a.HijoIzq().DiccionarioVacio()) {
			return a;
		} else {
			return menor(a.HijoIzq());
		}
	}

	public void EliminarValor(int clave, String valor) {
		if (raiz != null) {
			if (raiz.clave == clave) {
				raiz.valores.Sacar(valor);
				//AKA SE AGREGO SOLO ESTO.. que luego deborar el valor si el queda conjunto vacio, elimina el nodo.
				if (raiz.valores.ConjuntoVacio()) {
					this.Eliminar(clave);
				}
			} else if (raiz.clave < clave) {
				raiz.hijoDer.EliminarValor(clave, valor);
			} else {
				raiz.hijoIzq.EliminarValor(clave, valor);
			}
		}

	}

}